package com.work.oauth.service.impl;

import com.work.device.feign.UserFeign;
import com.work.oauth.service.AuthService;
import com.work.oauth.util.AuthToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class AuthServiceImpl implements AuthService {

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private UserFeign userFeign;

    @Value("${auth.ttl}")
    private long ttl;

    @Override
    public AuthToken login(String username, String password, String clientId, String clientSecret) {

        //1.1 远程根据服务名称获取服务实例对象
        ServiceInstance instance = loadBalancerClient.choose("user-auth");

        //获取ip地址  获取ip地址http://localhost:9008
        URI uri = instance.getUri();

        String url = uri + "/oauth/token";

        //1.2封装数据，body和header

        //1.2.1封装body数据
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "password");
        body.add("username", username);
        body.add("password", password);

        //1.2.2封装header数据
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        headers.add("Authorization", this.getHttpBasic(clientId, clientSecret));

        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(body, headers);

        //友好的体验
        //指定 restTemplate当遇到400或401响应时候也不要抛出异常，也要正常返回值
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                if (response.getRawStatusCode() != 400 && response.getRawStatusCode() != 401) {
                    super.handleError(response);
                }
            }
        });
         /*
            1.发送请求
               参数1表示请求的路径地址 http://localhost:9008/oauth/token
               参数2表示请求方式
               参数3表示封装的参数
               参数4表示封装成什么类型
         */

        ResponseEntity<Map> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, Map.class);

        //2.获取响应结果
        Map map = responseEntity.getBody();

        if (map == null || map.get("access_token") == null || map.get("refresh_token") == null || map.get("jti") == null) {

            //用户名密码输错5次锁定账号
            Long count = redisTemplate.opsForValue().increment(username);

            //对当前的key设置一个10分钟的有效期
            redisTemplate.expire(username, 10, TimeUnit.MINUTES);

            if (count >= 5) {
                //修改数据库字段,锁定账户
                userFeign.edit(username);
                throw new RuntimeException("账号已被锁定,请联系管理员初始化密码");
            }

            throw new RuntimeException("申请令牌失败");
        }

        //3.封装数据

        AuthToken authToken = new AuthToken();
        authToken.setAccessToken(String.valueOf(map.get("access_token")));
        authToken.setJti(String.valueOf(map.get("jti")));
        authToken.setRefreshToken(String.valueOf(map.get("refresh_token")));

        //4.把token封装到redis中
        redisTemplate.boundValueOps(authToken.getJti()).set(authToken.getAccessToken(), ttl, TimeUnit.SECONDS);
        return authToken;
    }

    //1.2.2.1进行http basic认证操作，把客户端id和客户端密码进行拼接，然后用base64进行编码
    private String getHttpBasic(String clientId, String clientSecret) {
        //进行拼接
        String value = clientId + ":" + clientSecret;
        byte[] encode = Base64Utils.encode(value.getBytes());
        return "Basic " + new String(encode);
    }
}
